import os
import socket
import threading
import traceback
from gglib.handler import handle
import queue
from gglib.session import Session, SessionClosedError
from gglib.settings import IO, NUM_WORKERS
import threadpool
from gglib.utils import import_attr

IO_READ = 1 << 0
IO_WRITE = 1 << 1


class IOSelector:
    def register_event(self, fd, ev):
        raise NotImplementedError

    def unregister_event(self, fd):
        raise NotImplementedError

    def wait_events(self, timeout=None):
        raise NotImplementedError


selector = import_attr(IO['ENGINE'])()

_read_fd, _write_fd = os.pipe()


def wakeup_selector():
    os.write(_write_fd, b'0')


def loop():
    # 创建工作者线程池，与请求队列
    thread_pool = threadpool.ThreadPool(NUM_WORKERS)
    packet_queue = queue.Queue(10000)

    def wait_request_to_process():
        print("The worker {} is ready".format(threading.current_thread().ident))
        while True:
            session, pkt = packet_queue.get(block=True)
            # 为每个会话做线程同步
            with session:
                try:
                    handle(session, pkt)
                except Exception as e:
                    traceback.print_exc()

    # 启动工作者线程并等待请求去处理
    for t in range(NUM_WORKERS):
        request = threadpool.WorkRequest(callable_=wait_request_to_process)
        thread_pool.putRequest(request)

    # 创建服务器套接口
    _tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置套接字选项
    _tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, IO['SZ_SEND_BUFFER'])
    _tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, IO['SZ_RECV_BUFFER'])
    _tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    _tcp_server.setblocking(False)

    _tcp_server.bind(IO['BIND_ADDRESS'])
    _tcp_server.listen(10)

    selector.register_event(_tcp_server.fileno(), IO_READ)
    selector.register_event(_read_fd, IO_READ)

    while True:
        # 使用IO复用技术
        for fd, ev in selector.wait_events(1):
            if ev & IO_READ:
                if fd == _tcp_server.fileno():
                    # 接受远程连接，并为该客户端创建会话
                    client, addr = _tcp_server.accept()
                    session = Session(client)
                    selector.register_event(client.fileno(), IO_READ)
                    continue
                elif fd == _read_fd:
                    os.read(_read_fd, 1)
                else:
                    session = Session.get_session_by_fd(fd)
                    if not session:
                        selector.unregister_event(fd)
                        continue
                    with session:
                        try:
                            # 处理会话，并队列化请求
                            pkt = session.recv_packet()
                            if pkt:
                                packet_queue.put((session, pkt))
                        except SessionClosedError:
                            # 清理客户端套接字对象
                            selector.unregister_event(fd)
                            session.close()


            elif ev & IO_WRITE:
                session = Session.get_session_by_fd(fd)
                with session:
                    pkt = session.next_packet()
                    if pkt:
                        session.sock.send(bytes(pkt))
                    else:
                        selector.unregister_event(session.sock.fileno())
                        selector.register_event(session.sock.fileno(), IO_READ)
